/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.diagram.composite.editpart;

import java.util.Iterator;

import org.eclipse.draw2d.XYLayout;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.commands.CompoundCommand;
import org.eclipse.gef.requests.ChangeBoundsRequest;
import org.eclipse.uml2.uml.Port;

import com.cea.papyrus.core.commands.GraphNodeSetConstraintCommand;
import com.cea.papyrus.core.editpart.GraphNodeEditPart;
import com.cea.papyrus.core.helper.NodeHelper;
import com.cea.papyrus.core.utils.Debug;
import com.cea.papyrus.core.utils.LookForElement;
import com.cea.papyrus.diagram.classdiagram.editpart.ClassEditPart;
import com.cea.papyrus.diagram.composite.command.UMLDi2ElementCreateFactory;
import com.cea.papyrus.diagram.composite.helper.NodePortHelper;
import com.cea.papyrus.diagraminterchange2.di2.GraphElement;
import com.cea.papyrus.diagraminterchange2.di2.GraphNode;
import com.cea.papyrus.diagraminterchange2.di2.Uml1SemanticModelBridge;

/**
 * Layout edit policy for the composite diagram.
 */
public class CompositeLayoutEditPolicy extends com.cea.papyrus.core.editpart.LayoutEditPolicy {

	/** Debug channel */
	protected static final int channel = 13;

	/**
	 * Creates a new @link CompositeLayoutEditPolicy
	 * @param layout layout for the composite diagram
	 */
	public CompositeLayoutEditPolicy(XYLayout layout) {
		super(layout);
	}

    /**
     * Return specific factory to provide specific commands
     */
    @Override
    protected UMLDi2ElementCreateFactory getCreateCommandFactory() {
        return UMLDi2ElementCreateFactory.eINSTANCE;
    }
    
	/**
	 * used to create an add command.
	 * @param childEditPart the editpart that is conecerned
	 * @param constraint to set on this editpart
	 * @param request to create an add command
	 * @return the addcommand
	 */

	protected Command createAddCommand(ChangeBoundsRequest request, EditPart childEditPart, 
			Object constraint) {

		CompoundCommand c= new CompoundCommand();
		//we cannot move a port into his class or property
		if(childEditPart instanceof PortEditPart){return org.eclipse.gef.commands.UnexecutableCommand.INSTANCE;}
		
		if(childEditPart instanceof ClassEditPart||childEditPart instanceof PropertyEditPart){
			GraphElement parentGE=(GraphElement)this.getHost().getModel();
			Iterator itgraphChild= ((GraphNodeEditPart)childEditPart).getGraphNode().getSemanticChild().iterator();
			while( itgraphChild.hasNext()){
				GraphNode gn= (GraphNode)itgraphChild.next();
				Rectangle r= new Rectangle(gn.getPosition().x,gn.getPosition().y, gn.getSize().width,gn.getSize().height);
				GraphNodeSetConstraintCommand locationCommand = new GraphNodeSetConstraintCommand(gn,
						request,
						request.getTransformedRectangle(r));
				c.add(locationCommand);
			}
			if (((GraphNodeEditPart) childEditPart).getGraphNode().getSemanticParent()!=parentGE){
				return null;
			}
		}
		c.add(super.createAddCommand(request, childEditPart, constraint));

		return c;
	}

	/**
	 * This method is used to set a new position of the graphNode when the parent GraphNode has moved.
	 * @param parentGraphNode the parentgraphNode that have moved.
	 * @param request the change Bound Request that the parent graphNode has received.
	 * @param portGraphNode the graphNode thath represent the port or the label of the GraphNode
	 * @return Rectangle that represents good position and size of the port
	 */
	public Rectangle calculateNewPosition( GraphNode parentGraphNode, GraphNode portGraphNode, ChangeBoundsRequest request){
		Rectangle rec= new Rectangle();
		//detection of Mouvement
		if( request.getSizeDelta().equals(new Dimension(0,0))){

			//apply translation
			rec.setSize(portGraphNode.getSize());
			rec.setLocation(portGraphNode.getPosition());
			rec.x=rec.x+request.getMoveDelta().x;
			rec.y=rec.y+request.getMoveDelta().y;
			return rec;			
		}
		//dectection of a resize
		else{

			//1 calculus of intern limit.
			Rectangle smallParentRect=new Rectangle(
					parentGraphNode.getPosition().x+request.getMoveDelta().x,
					parentGraphNode.getPosition().y+request.getMoveDelta().y,
					parentGraphNode.getSize().width+request.getSizeDelta().width,
					parentGraphNode.getSize().height+request.getSizeDelta().height
			);
			//caculus of extern limit
			Rectangle bigParentRect=new Rectangle(
					parentGraphNode.getPosition().x+request.getMoveDelta().x-1,
					parentGraphNode.getPosition().y+request.getMoveDelta().y-1,
					parentGraphNode.getSize().width+request.getSizeDelta().width+2,
					parentGraphNode.getSize().height+request.getSizeDelta().height+2
			);

			//2 calculus of the middle of the port
			Point portMiddlePosition = new Point(
					portGraphNode.getPosition().x+ portGraphNode.getSize().width/2,
					portGraphNode.getPosition().y+portGraphNode.getSize().height/2
			);

			//3 if the point is in the extern rectangle and not in the intern limit the port is well placed
			if ( (!smallParentRect.contains(portMiddlePosition))&& bigParentRect.contains(portMiddlePosition)){
				rec.setSize(portGraphNode.getSize());
				rec.setLocation(portGraphNode.getPosition());
				return rec;
			}
			//4 the port is bad placed
			else{
				NodePortHelper np=new NodePortHelper();
				NodeHelper nh= new NodeHelper();
				//we have to calculate position in the future position
				GraphNode futureNode=nh.createGraphElement(null, null, LookForElement.calculateAbsolutePosition(parentGraphNode,new Point(0,0)), parentGraphNode.getSize().getCopy());
				futureNode.setSize(futureNode.getSize().expand(request.getSizeDelta()));
				futureNode.setPosition(futureNode.getPosition().translate(request.getMoveDelta()));

				Debug.debug(this, "show position of future graphNode"+ futureNode.getPosition()+" "+futureNode.getSize(), channel);
				//calculate in absolute coordinate
				Point goodPosition=np.inRunner(futureNode,LookForElement.calculateAbsolutePosition(portGraphNode,new Point(0,0)), portGraphNode.getSize());
				// translate to relative
				Point origin=LookForElement.calculateAbsolutePosition(((GraphNode)parentGraphNode.getContainer()),new Point(0,0));
				goodPosition= goodPosition.getTranslated(origin.getNegated().getCopy());
				rec.setLocation(goodPosition);
				rec.setSize(portGraphNode.getSize());
				Debug.debug(this, "show new position of the port: "+rec.getLocation(), channel);
				return rec;
			}
		}
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.common.editpart.LayoutEditPolicy#createChangeConstraintCommand(org.eclipse.gef.requests.ChangeBoundsRequest, org.eclipse.gef.EditPart, java.lang.Object)
	 */
	@Override
	protected Command createChangeConstraintCommand(ChangeBoundsRequest request, 
			EditPart child, Object constraint) {
		GraphNodeSetConstraintCommand locationCommand;
		CompoundCommand command= new CompoundCommand();

		//first move the conecerned Editpart
		locationCommand = new GraphNodeSetConstraintCommand((GraphNode)child.getModel(), request, (Rectangle)constraint);
//		locationCommand = new GraphNodeSetConstraintCommand();
//		locationCommand.setPart((GraphNode)child.getModel());
//		locationCommand.setLocation((Rectangle)constraint);
		command.add(locationCommand);

		//if this class move owned ports
		if((child instanceof com.cea.papyrus.diagram.composite.editpart.ClassEditPart)||
				(child instanceof com.cea.papyrus.diagram.composite.editpart.PropertyEditPart)){

			GraphNode parentGraphNode=((com.cea.papyrus.core.editpart.GraphNodeWithSemanticEditPart)child).getGraphNode();
			//itgn iterator of graphNode
			Iterator itgn=parentGraphNode.getSemanticChild().iterator();

			while (itgn.hasNext()){
				GraphElement ge=(GraphElement)itgn.next();
				if (ge.getSemanticModel()!=null&&((Uml1SemanticModelBridge)ge.getSemanticModel()).getElement() instanceof Port){

					//ge is a graphNode of a port
					GraphNode portgn=(GraphNode)ge;
					// calculate new position of the port
					Rectangle rec=calculateNewPosition(
							parentGraphNode,
							portgn,
							request);
					locationCommand = new GraphNodeSetConstraintCommand(portgn, request, rec);

					// add this command in the list of commands
					command.add(locationCommand);
					//look for all portNameEditPArt graphNode
					Iterator itPortName=portgn.getSemanticChild().iterator();
					Dimension diff=rec.getLocation().getDifference(portgn.getPosition());
					//look for all graphNode that is connected to propertygn
					while (itPortName.hasNext()){
						GraphNode gePortName=(GraphNode)itPortName.next();
						//Create a move command for this element
						rec =new Rectangle(gePortName.getPosition(),gePortName.getSize());
						rec=rec.getTranslated(diff.width,diff.height);

						locationCommand = new GraphNodeSetConstraintCommand((GraphNode)gePortName, request, rec);
						// add this command in the list of commands
						command.add(locationCommand);
					}
				}	
			}
			displayCompoundCommand(command);
			return command;
		}// end of part


//		case of a port
		if (child instanceof PortEditPart) {
			PortEditPart porteditpart = (PortEditPart) child;
			GraphNode portgn=(porteditpart).getGraphNode();

			//look for all portNameEditPArt graphNode
			Iterator itgn=portgn.getSemanticChild().iterator();

			//look for all gephNode that is connected to propertygn
			while (itgn.hasNext()){
				GraphElement ge=(GraphElement)itgn.next();
//				Create a move command for this element
				//calculate new position of the port
				Rectangle rec= new Rectangle();
				rec.x=((GraphNode)ge).getPosition().x+request.getMoveDelta().x+request.getSizeDelta().width;
				rec.y=((GraphNode)ge).getPosition().y+request.getMoveDelta().y+request.getSizeDelta().height;
				rec.setSize(((GraphNode)ge).getSize());
				locationCommand = new GraphNodeSetConstraintCommand((GraphNode)ge, request, rec);
//				locationCommand.setPart((GraphNode)ge);
//				locationCommand.setLocation(rec);
				// add this command in the list of commands
				command.add(locationCommand);

			}
			return command;
		}
		return super.createChangeConstraintCommand(request, child, constraint);
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.common.editpart.LayoutEditPolicy#getAddCommand(org.eclipse.gef.Request)
	 */


//	/* (non-Javadoc)
//	* @see com.cea.papyrus.common.editpart.LayoutEditPolicy#getCreateCommand(org.eclipse.gef.requests.CreateRequest)
//	*/
//	@Override
//	protected Command getCreateCommand(CreateRequest request) {
//	Object newObjectDescription = EnhancedType.getNewObjectDescription(request);
//	PapyrusCommand createCommand = null;
//	Point p;
//	Debug.debug(this,"CreateCommand: for "+ newObjectDescription+" at "+ request.getLocation()+ " "+request.getSize() ,channel);


//	createCommand = new com.cea.papyrus.diagram.composite.command.GraphNodeCreateCommand(newObjectDescription);
//	createCommand.setParent((GraphNode)getHost().getModel());
//	p= request.getLocation();

//	// If accelerator is used, the position is null
//	if (p!=null){
//	//the element creation is done withe a position 
//	if (!(newObjectDescription.equals(org.eclipse.uml2.uml.Port.class))){
//	getLayoutContainer().translateToRelative(p);
//	getLayoutContainer().translateFromParent(p);
//	p.translate(getLayoutOrigin().getNegated());
//	}
//	createCommand.setLocation(p);
//	}
//	else{
//	// If an element has no position, its defaut position wil be (30, 30)
//	createCommand.setLocation(new Point(30, 30));
//	}
//	if( request.getSize()!=null){
//	createCommand.setSize(request.getSize());
//	}
//	return createCommand;
//	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.common.editpart.LayoutEditPolicy#createLabelAddCommand(org.eclipse.gef.requests.ChangeBoundsRequest, com.cea.papyrus.diagraminterchange2.di2.GraphNode, java.lang.Object)
	 */
	@Override
	protected Command createLabelAddCommand(ChangeBoundsRequest request,GraphNode graphNode,Object constraint) {
		CompoundCommand cmd = new CompoundCommand();
		if( graphNode.getSemanticParent()!=null &&
				((Uml1SemanticModelBridge)((GraphNode)graphNode.getSemanticParent()).getSemanticModel()).getElement() instanceof Port){

			GraphNodeSetConstraintCommand locationCommand = new GraphNodeSetConstraintCommand(graphNode, request, (Rectangle)constraint);
			cmd.add(locationCommand);}
		// graphElements that represent label don't have to move another graphElement

		else 
		{cmd.add(super.createLabelAddCommand(request, graphNode, constraint));}
		return cmd;
	}

}
